/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.graph.algo.ksp.srdrPaths;

import combi.graph.Mask;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.*;

import combi.sipper.io.IOSimpleGraph;
import combi.utils.Couple;
import combi.wip.graph.algo.ksp.LabelGroupeSommets;

import org.jgrapht.graph.DefaultDirectedWeightedGraph;
//import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DefaultWeightedEdge;


public class Ksp_Yen_SRDR_P2P_mt {

	private static int NB_THREADS = 3;
	private String fileIn;
	private Integer k = 10;
	private String outFile;
	private String stats;
	private DefaultDirectedWeightedGraph<Integer, DefaultWeightedEdge> graph;

	private List<Couple<Set<Integer>, Set<Integer>>> queries;
	private Map<Integer, Set<Integer>> twins;
	private LabelGroupeSommets<Set<Integer>> labels=null;	
	private boolean useRank;	
	private double lengthPathLimit = Double.POSITIVE_INFINITY;

	public Ksp_Yen_SRDR_P2P_mt(String graph, List<Couple<Set<Integer>, Set<Integer>>> queries, int k, Map<Integer, Set<Integer>> twins, boolean useRank, String outFile)
	{
		this.fileIn = graph;
		this.outFile = outFile;
		String prefixe = outFile.substring(0, outFile.lastIndexOf("."));
		this.stats = prefixe + "_stats.txt";
		this.k = k;
		this.setQueries(queries);
		this.twins = twins;
		this.useRank = useRank;
	}
	
	public static void set_NB_THREADS(int nBTHREADS) {
		NB_THREADS = nBTHREADS;
	}

	public void setLabelsGroupes(LabelGroupeSommets<Set<Integer>> labels)
	{
		this.labels = labels;
	}

	public void setQueries(List<Couple<Set<Integer>, Set<Integer>>> queries)
	{
		this.queries = queries;
	}

	public List<Integer> removeIsolatedVertices(DefaultDirectedWeightedGraph<Integer, DefaultWeightedEdge> graph)
	{
		// filtrage des sommets isoles
		List<Integer> isole = new LinkedList<Integer>();
		for(int i : graph.vertexSet())
		{
			if (graph.inDegreeOf(i) == 0 && graph.outDegreeOf(i) ==0)
			{
				isole.add(i);
			}
		}
		for(int i : isole)
		{
			graph.removeVertex(i);
		}
		return isole;
	}


	/**
	 * @param args
	 */
	public void run() {

		Date deb = new Date(java.lang.System.currentTimeMillis());

		try{
			this.graph = IOSimpleGraph.load(fileIn);
			int number = graph.vertexSet().size(); // nombre de sommets
			removeIsolatedVertices(graph).clear();
			
			
			Mask<Integer, DefaultWeightedEdge> mask = new Mask<Integer, DefaultWeightedEdge>();

			//ajout d'une source factice reliee a tous les sommets.
			Integer fakeSrc = -1;
			graph.addVertex(fakeSrc);
			for (int n = 0; n <number; n++)
			{
				if (graph.containsVertex(n))
				{
					graph.addEdge(fakeSrc, n);
					graph.setEdgeWeight(graph.getEdge(fakeSrc, n), 0);
					mask.addEdge(graph.getEdge(fakeSrc, n));
				}
			}

			//ajout d'une destination factice reliee a tous les sommets.
			Integer fakeDest = -2;
			graph.addVertex(fakeDest);
			for (int n = 0; n <number; n++)
			{
				if (graph.containsVertex(n))
				{
					graph.addEdge(n, fakeDest);
					graph.setEdgeWeight(graph.getEdge(n, fakeDest), 0);
					mask.addEdge(graph.getEdge(n, fakeDest));
				}
			}

			// ajout des fakeSrc de fakeDest pour twins:
			twins.put(fakeSrc, Collections.singleton(fakeSrc));
			twins.put(fakeDest, Collections.singleton(fakeDest));
			
			BufferedWriter out = new BufferedWriter(new FileWriter(outFile));
			out.close();
			BufferedWriter outStats = new BufferedWriter(new FileWriter(stats));
			outStats.write("Begin : \t" +new Date(java.lang.System.currentTimeMillis())+"\n");
			outStats.close();
			
			Thread t = new ControleCheminsEnParalleleSRDR(NB_THREADS,graph, fakeSrc, fakeDest, mask, k, twins,
					queries, labels, useRank, outFile);
			if (lengthPathLimit>=0)
			((ControleCheminsEnParalleleSRDR) t).setPathLengthLimit(lengthPathLimit);
			t.start();
			t.join();
			

			// retrait des fakeSrc de fakeDest de twins:
			//twins.remove(fakeSrc);
			//twins.remove(fakeDest);
			
			outStats = new BufferedWriter(new 
					FileWriter(stats, true));
			Date fin = new Date(java.lang.System.currentTimeMillis());

			outStats.write("End : \t" +
					fin +"\n"
					+ "Duration : " + (fin.getTime()-deb.getTime()) + "ms");
			outStats.close();

		}
		catch(Exception e){
			e.printStackTrace();
			System.exit(1);
		}
		finally
		{			
			Date fin = new Date(java.lang.System.currentTimeMillis());
			System.out.println("Begins at " +deb);
			System.out.println("Ends at " + fin);
			System.out.println("Duration " + (fin.getTime()-deb.getTime()) + "ms");
			//System.err.println("End");
		}
	}

	public void setPathLengthLimit(double limit) {
		// TODO Auto-generated method stub
		lengthPathLimit = limit;
	}


	public static List<DefaultWeightedEdge> cut (List<DefaultWeightedEdge> l)
	{
		l.remove(l.size()-1);
		l.remove(0);
		return l;
	}

	public static List<DefaultWeightedEdge> removeFakeElements(List<DefaultWeightedEdge> path)
	{
		path.remove(0);
		path.remove(path.size()-1);
		return path;
	}



}
